/*
 * Copyright 2010-2012 Susanta Tewari. <freecode4susant@users.sourceforge.net>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package genomemap.map;

import commons.util.GUIUtil;
import commons.util.Util;
import org.apache.commons.lang.ArrayUtils;
import test.genomemap.data.GenomemapTestData;

import javax.swing.*;
import java.awt.*;
import java.util.*;
import java.util.List;

import static com.google.common.collect.ImmutableList.copyOf;

/**
 * @deprecated use {@link genomemap.map.AlignmentMap#getComponent()} instead
 * @version 1.0 2006
 * @author Susanta Tewari
 */
@Deprecated
public class AlignmentMapPanel extends JPanel {

    private final int MAP_HEADER_VERT_INDENT = 40;
    private final int INTER_LABEL_VERT_GAP   = 5;
    private final Font textFont              = new Font("Courier New", Font.BOLD, 12);
    private final Color lineColor            = Color.RED;
    private final Stroke lineStroke = new BasicStroke(2.5f, BasicStroke.CAP_ROUND,
                                          BasicStroke.JOIN_ROUND);
    private int mapWIDTH                     = 0;
    private int mapHEIGHT                    = 0;
    private int[][] MAP_STR_LEN              = null;
    private int[] MAP_X                      = null;
    private int mapTotal                     = 0;
    private String[] mapHeaders              = null;

    /**
     * anyCross has length = mapTotal - 1. boolean values indicate if
     * any lines should be drawn between the maps. provide null to
     * ignore crossing. //TODO rather than between consecutive maps,
     * provision should be there between any 2 maps
     */
    private boolean[] anyCross = null;
    private int[] crossCounts  = null;
    private String[][] labels  = null;
    private int LABEL_HEIGHT;

    public AlignmentMapPanel() {}

    /**
     *
     * @param maps alignment maps
     * @param useCrosses {@code true} if maps should be crossed; {@code false} otherwise
     * @throws MapException if the common data is empty
     */
    public AlignmentMapPanel(final List<? extends GenomeMap> maps, final boolean useCrosses)
            throws MapException {

        mapTotal   = maps.size();
        labels     = new String[maps.size()][];
        mapHeaders = new String[maps.size()];

        final Collection<String> commonSet = new HashSet<>(maps.get(0).getLabels());

        for (int i = 0; i < mapTotal; i++) {
            commonSet.retainAll(maps.get(i).getLabels());
        }

        if (commonSet.size() == 0) {
            throw new MapException("common set is empty");
        }

        for (int i = 0; i < mapTotal; i++) {

            final Collection originalSet = new LinkedHashSet(maps.get(i).getLabels());

            originalSet.retainAll(commonSet);

            this.labels[i] = (String[]) originalSet.toArray(new String[0]);
            mapHeaders[i]  = maps.get(i).getTitle();
        }

        anyCross = new boolean[labels.length - 1];

        Arrays.fill(anyCross, useCrosses);
        instantiate();
    }

    /**
     * count of crossing will be measured only for "common" maps
     */
    private void instantiate() {

        LABEL_HEIGHT = getFontMetrics(textFont).getHeight();

        final int MAP_HEADER_HORI_INDENT = Util.findMax(pixelWidth(mapHeaders, this.textFont)) / 2;

        MAP_STR_LEN = new int[mapTotal][];
        MAP_X       = new int[mapTotal];

        final int INTER_MAP_GAP = 150;

        for (int i = 0; i < mapTotal; i++) {

            MAP_STR_LEN[i] = pixelWidth(labels[i], textFont);

            if (i == 0) {
                MAP_X[0] = MAP_HEADER_HORI_INDENT;
            } else {
                MAP_X[i] = MAP_X[i - 1] + Util.findMax(this.MAP_STR_LEN[i - 1]) + INTER_MAP_GAP;
            }

            mapWIDTH += Util.findMax(MAP_STR_LEN[i]);
        }

        mapHEIGHT = 2 * MAP_HEADER_VERT_INDENT + (labels[0].length - 1) * INTER_LABEL_VERT_GAP
                    + labels[0].length * LABEL_HEIGHT;
        mapWIDTH += 2 * MAP_HEADER_HORI_INDENT + (mapTotal - 1) * INTER_MAP_GAP;

        setPreferredSize(new Dimension(mapWIDTH, mapHEIGHT));
        setBackground(Color.white);
    }

    @Override
    public void paintComponent(final Graphics g) {

        super.paintComponent(g);

        final Graphics2D g2 = (Graphics2D) g;

        g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        g2.setStroke(lineStroke);


        // create background
        g2.setColor(Color.white);
        g2.fillRect(0, 0, mapWIDTH, mapHEIGHT);


        // set up text color and font and rendering hints
        g2.setColor(Color.black);
        g2.setFont(textFont);

        for (int i = 0; i < mapTotal; i++) {


            // draw map header
            g2.drawString(mapHeaders[i],
                          MAP_X[i] + pixelWidth(labels[i][0], textFont) / 2
                          - pixelWidth(mapHeaders[i], textFont) / 2, MAP_HEADER_VERT_INDENT / 2);


            // draw map
            for (int labelIndex = 0; labelIndex < labels[i].length; labelIndex++) {

                g2.drawString(labels[i][labelIndex], MAP_X[i],
                              MAP_HEADER_VERT_INDENT
                              + labelIndex * (LABEL_HEIGHT + INTER_LABEL_VERT_GAP));
            }
        }


        // draw lines where applicable
        if (anyCross == null) return;

        g2.setStroke(lineStroke);
        g2.setColor(lineColor);

        int crossMapIndex = 0;

        for (int i = 1; i < mapTotal; i++) {

            if (!anyCross[crossMapIndex]) continue;

            for (int labelIndex = 0; labelIndex < labels[i - 1].length; labelIndex++) {

                final int index          = ArrayUtils.indexOf(labels[i], labels[i - 1][labelIndex]);
                final int LABEL_LINE_GAP = 5;
                final int x1 = MAP_X[i - 1] + MAP_STR_LEN[i - 1][labelIndex] + LABEL_LINE_GAP;
                final int y1 = MAP_HEADER_VERT_INDENT
                               + labelIndex * (LABEL_HEIGHT + INTER_LABEL_VERT_GAP) - 2;
                final int x2             = MAP_X[i] - LABEL_LINE_GAP;
                final int y2 = MAP_HEADER_VERT_INDENT
                               + index * (LABEL_HEIGHT + INTER_LABEL_VERT_GAP) - 2;

                g2.drawLine(x1, y1, x2, y2);
            }

            crossMapIndex++;
        }
    }

    @Override
    public int getHeight() {
        return mapHEIGHT;
    }

    @Override
    public int getWidth() {
        return mapWIDTH;
    }

    private int[] pixelWidth(final String[] words, final Font font) {

        final int[] temp = new int[words.length];

        for (int i = 0; i < words.length; i++) {
            temp[i] = pixelWidth(words[i], font);
        }

        return temp;
    }

    private int pixelWidth(final String word, final Font font) {
        return (this.getFontMetrics(font).stringWidth(word));
    }

    /**
     * @return list of cross-counts (immutable)
     */
    public List<Integer> getCrossCounts() {

        if (crossCounts != null) return copyOf(ArrayUtils.toObject(crossCounts));

        crossCounts = new int[anyCross.length];

        if (anyCross == null) return copyOf(ArrayUtils.toObject(crossCounts));

        int crossMapIndex = 0;

        for (int i = 1; i < mapTotal; i++) {

            if (!anyCross[crossMapIndex]) continue;

            for (int labelIndex = 0; labelIndex < labels[i - 1].length; labelIndex++) {

                final int index = ArrayUtils.indexOf(labels[i], labels[i - 1][labelIndex]);

                if (index < 0) {

                    final String message = "line: map" + (i - 1) + " -> map" + i + ". label: "
                                           + labels[i - 1][labelIndex] + " not found";

                    throw new RuntimeException(message);
                }

                if (index != labelIndex) crossCounts[crossMapIndex]++;
            }

            crossMapIndex++;
        }

        return copyOf(ArrayUtils.toObject(crossCounts));
    }

    public static void main(String[] args) {

        final List<GenomeMap> maps = new ArrayList<>(2);

        maps.add(GenomemapTestData.dataSet.get("D1"));
        maps.add(GenomemapTestData.dataSet.get("D2"));

        final AlignmentMapPanel alignmentMap = new AlignmentMapPanel(maps, true);

        GUIUtil.displayGUI("", alignmentMap);
    }
}
